package ru.spb.nicetu.altx.mnemo.extpoint.plugin;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellEditorValidator;
import org.eclipse.osgi.framework.internal.core.BundleURLConnection;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

import ru.spb.nicetu.altx.mnemo.cell.IDefaultValue;
import ru.spb.nicetu.altx.mnemo.cell.ManagedNodeCell;
import ru.spb.nicetu.altx.mnemo.cell.SettingNodeCell;
import ru.spb.nicetu.altx.mnemo.cell.INodeCell.Categories;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.ISimpleFunc;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.ISimpleFuncFactory;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.SimpleFuncFactory;
import ru.spb.nicetu.altx.mnemo.extpoint.model.typeConverter.IConverter;
import ru.spb.nicetu.altx.mnemo.extpoint.valuetypes.ITemplateValueType;
import ru.spb.nicetu.altx.mnemo.math.managers.CellEditClassManager;
import ru.spb.nicetu.altx.mnemo.math.managers.NodeFactoryManager;
import ru.spb.nicetu.altx.mnemo.math.managers.SimpleFuncFactoryManager;
import ru.spb.nicetu.altx.mnemo.math.managers.ValueTypeClassManager;
import ru.spb.nicetu.altx.mnemo.math.managers.ValueTypeConverterManager;
import ru.spb.nicetu.altx.mnemo.node.factory.DataNodeFactory;
import ru.spb.nicetu.altx.mnemo.node.factory.INodeFactory;
import ru.spb.nicetu.altx.mnemo.node.factory.TaskNodeFactory;

/**
 * The activator class controls the plug-in life cycle
 */
@SuppressWarnings("restriction")
public class Activator extends AbstractUIPlugin {

  // The plug-in ID
  public static final String PLUGIN_ID = "prop_ext_point";

  // The plug-in extention points ID
  public static final String EXTPOINT_ID_simpleFunction = "proto.mnemo.extpoint.point.simpleFunction";
  public static final String EXTPOINT_ID_valueType = "proto.mnemo.extpoint.point.valueType";
  public static final String EXTPOINT_ID_typeConverter = "ru.spb.nicetu.altx.extpoint.point.typeConverter";

  // The shared instance
  private static Activator plugin;

  private final String TAG_NAME = "name";
  private final static String TAG_CLASS = "class";
  private final String TAG_COMMENT = "comment";
  private final String TAG_DESCRIPTION = "description";
  private final String TAG_CATEGORY = "category";

  private final String TAG_COMPOSITE_CLASS = "compositeClass";
  private final String TAG_CELL_EDITOR_CLASS = "cellEditorClass";
  private final String TAG_VALIDATOR_CLASS = "validatorClass";
  private final String TAG_PARAMLOADER_CLASS = "paramConverterClass";
  private final String TAG_FIGURE_CLASS = "figureClass";
  private final String TAG_VALUE_VALIDATOR_CLASS = "valueValidatorClass";
  private final String TAG_HIDDEN = "hidden";
  private final String TAG_DBALIAS = "dbAlias";

  private final String TAG_PROPERTY = "property";
  private final String TAG_PROPERTY_KEY = "name";
  private final String TAG_PROPERTY_VALUE = "value";
  private final String TAG_DEFAULT_VALUE = "defaultValue";

  private final String TAG_TASK_TYPE = "taskType";
  private final String TAG_IDCELL = "IDcell";
  private final String TAG_SETTING = "setting";
  private final String TAG_INPUT = "input";
  private final String TAG_OUTPUT = "output";

  private final String TAG_FROM_CLASS = "fromClass";
  private final String TAG_TO_CLASS = "toClass";

  private static final String TAG_COMMENT_TEXT = "text";

  public Activator() {
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework
   * .BundleContext )
   */
  @Override
  public void start(BundleContext context) throws Exception {
    super.start(context);
    plugin = this;

    buildValueTypeExtension();
    buildSimpleFunctionExtension();
    buildConverterExtension();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework
   * .BundleContext )
   */
  @Override
  public void stop(BundleContext context) throws Exception {
    plugin = null;
    super.stop(context);
  }

  /**
   * @return the shared instance
   */
  public static Activator getDefault() {
    return plugin;
  }

  /**
   * Сборка расширений 'valueType'
   */
  @SuppressWarnings( { "unchecked", "static-access" })
  private void buildValueTypeExtension() {
    // Platform.getBundle("as");
    IExtensionRegistry registry = Platform.getExtensionRegistry();

    // Ищем только НАШИ ('proto.mnemo.extpoint.point.valueType') расширения
    for (IConfigurationElement conf : registry
        .getConfigurationElementsFor(EXTPOINT_ID_valueType)) {
      try {
        Object o;
        try {
          o = conf.createExecutableExtension(TAG_CLASS);
        } catch (CoreException e) {
          e.printStackTrace();
          continue;
        }

        // Учитываем только те расширения, класс которых нас удовлетворяет
        if (!(o instanceof ITemplateValueType)) {
          continue;
        }

        INodeFactory nodeFactory = new DataNodeFactory();

        ITemplateValueType valueType = (ITemplateValueType) o;

        Class classClass = valueType.getClass();

        // String className = classClass.toString();
        String className = conf.getAttribute(TAG_CLASS);
        System.out.println(className);

        String compositeClassName = conf.getAttribute(TAG_COMPOSITE_CLASS);
        String paramLoaderClassName = conf.getAttribute(TAG_PARAMLOADER_CLASS);
        String figureClassName = conf.getAttribute(TAG_FIGURE_CLASS);
        String valueValidatorClassName = conf
            .getAttribute(TAG_VALUE_VALIDATOR_CLASS);
        String dbAlias = conf.getAttribute(TAG_DBALIAS);
        String hiddenStr = conf.getAttribute(TAG_HIDDEN);
        boolean hidden = Boolean.TRUE.toString().equals(hiddenStr);

        Class<?> compositeClass = null;
        Class<?> paramLoaderClass = null;
        Class<?> figureClass = null;
        Class<?> valueValidatorClass = null;
        Class cellEditorClass = null;
        ClassLoader loader = valueType.getClass().getClassLoader();
        if (compositeClassName != null) {
          try {
            compositeClass = o.getClass().forName(compositeClassName, true,
                loader);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
          if (compositeClass != null) {
            ValueTypeClassManager.getInstance().addCompositeClass(className,
                compositeClass);
          }
        }
        if (paramLoaderClassName != null) {
          try {
            paramLoaderClass = o.getClass().forName(paramLoaderClassName, true,
                loader);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
          if (paramLoaderClass != null) {
            ValueTypeClassManager.getInstance().addParamConverterClass(
                className, paramLoaderClass);
          }
        }
        if (figureClassName != null) {
          try {
            figureClass = o.getClass().forName(figureClassName, true, loader);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
          if (figureClass != null) {
            ValueTypeClassManager.getInstance().addFigureClass(className,
                figureClass);
          }
        }
        if (valueValidatorClassName != null) {
          try {
            valueValidatorClass = o.getClass().forName(valueValidatorClassName,
                true, loader);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
          if (valueValidatorClass != null) {
            ValueTypeClassManager.getInstance().addValueValidatorClass(
                className, valueValidatorClass);
          }
        }
        try {
          Object editorClassO = conf
              .createExecutableExtension(TAG_CELL_EDITOR_CLASS);
          if (editorClassO instanceof CellEditor) {
            cellEditorClass = ((CellEditor) editorClassO).getClass();
            ValueTypeClassManager.getInstance().addCellEditorClass(
                classClass.getName(), cellEditorClass);
          }
        } catch (CoreException e) {
        } catch (InvalidRegistryObjectException e) {
        }
        ValueTypeClassManager.getInstance().addDBAlias(className, dbAlias);
        ValueTypeClassManager.getInstance().addValueTypeClass(className,
            classClass);

        if (!hidden) {
          String name = valueType.getName();
          nodeFactory.setName(name);
          nodeFactory.addNodeCell(new ManagedNodeCell("1", "valueIn",
              className, Categories.INPUT));
          nodeFactory.addNodeCell(new ManagedNodeCell("2", "valueOut",
              className, Categories.OUTPUT));

          NodeFactoryManager.getInstance().addNodeFactory(nodeFactory);
        }

        // Считываем дополнительные Свойства типа
        IConfigurationElement[] children = null;
        children = conf.getChildren(TAG_PROPERTY);
        for (IConfigurationElement child : children) {
          String key = child.getAttribute(TAG_PROPERTY_KEY);
          String value = child.getAttribute(TAG_PROPERTY_VALUE);
          ValueTypeClassManager.getInstance()
              .addProperty(className, key, value);
        }
      } catch (InvalidRegistryObjectException e) {
        e.printStackTrace();
      }
    }// for(IConfigurationElement conf : ...
  }

  /**
   * Сборка расширений "simpleFunction"
   */
  @SuppressWarnings( { "unchecked", })
  private void buildSimpleFunctionExtension() {
    IExtensionRegistry registry = Platform.getExtensionRegistry();

    // Ищем только НАШИ ("proto.mnemo.extpoint.point.simpleFunction") расширения
    for (IConfigurationElement conf : registry
        .getConfigurationElementsFor(EXTPOINT_ID_simpleFunction)) {
      try {
        Object o = conf.createExecutableExtension(TAG_CLASS);

        // ClassLoader
        // Учитываем только те расширения, класс которых нас удовлетворяет
        if (o instanceof ISimpleFunc) {
          String name = conf.getAttribute(TAG_NAME);
          String taskType = conf.getAttribute(TAG_TASK_TYPE);

          IConfigurationElement[] children = null;
          
          String commentText = "";
          
          children = conf.getChildren(TAG_DESCRIPTION);
          if(children.length>0){
            commentText = children[0].getValue();
          } else {
            String commentAttribute = conf.getAttribute(TAG_COMMENT);
            commentText = readCommentText(conf, commentAttribute);            
          }
          
          INodeFactory nodeFactory = new TaskNodeFactory(
              o.getClass().getName(), taskType, commentText);

          nodeFactory.setName(name);
          
          // считываем КАТЕГОРИИ
          children = conf.getChildren(TAG_CATEGORY);
          for (IConfigurationElement child : children) {
            String childName = child.getAttribute(TAG_NAME);

            nodeFactory.addCategory(childName);
          } // for(IConfigurationElement child : children)
          
          // считываем ВХОДЫ, т.е. "имена формальных параметров"
          children = conf.getChildren(TAG_INPUT);
          for (IConfigurationElement child : children) {
            String childName = child.getAttribute(TAG_NAME);
            String childClassName = child.getAttribute(TAG_CLASS);
            String childID = child.getAttribute(TAG_IDCELL);

            nodeFactory.addNodeCell(new ManagedNodeCell(childID, childName,
                childClassName, Categories.INPUT));
          } // for(IConfigurationElement child : children)

          // считываем ВЫХОДЫ
          children = conf.getChildren(TAG_OUTPUT);
          for (IConfigurationElement child : children) {
            String childName = child.getAttribute(TAG_NAME);
            String childClassName = child.getAttribute(TAG_CLASS);
            String childID = child.getAttribute(TAG_IDCELL);

            nodeFactory.addNodeCell(new ManagedNodeCell(childID, childName,
                childClassName, Categories.OUTPUT));
          }

          // считываем НАСТРОЕЧНЫЕ ПАРАМЕТРЫ
          children = conf.getChildren(TAG_SETTING);
          for (IConfigurationElement child : children) {

            Class<? extends CellEditor> editorClass = null;
            ICellEditorValidator editorValidator = null;

            String childID = child.getAttribute(TAG_IDCELL);
            String childName = child.getAttribute(TAG_NAME);
            String childClassName = child.getAttribute(TAG_CLASS);
            Object cellVal = child.createExecutableExtension(TAG_CLASS);
            if (cellVal instanceof ITemplateValueType) {
              ITemplateValueType value = (ITemplateValueType) cellVal;
              ValueTypeClassManager.getInstance().addValueTypeClass(
                  value.getClass().getName(), value.getClass());
            }

            try {
              Object editorClassO = child
                  .createExecutableExtension(TAG_CELL_EDITOR_CLASS);
              if (editorClassO instanceof CellEditor) {
                editorClass = ((CellEditor) editorClassO).getClass();
              }
            } catch (Exception e) {
              // System.out.println("Не задан CellEditor для "
              // + name + "." + childName);
            }

            try {
              Object obj = child.createExecutableExtension(TAG_VALIDATOR_CLASS);
              if (obj instanceof ICellEditorValidator) {
                editorValidator = (ICellEditorValidator) obj;
              }
            } catch (Exception e) {
              // System.out.println("Не задан CellEditorValidator для " + name +
              // "." + childName);
            }

            CellEditClassManager.getInstance().addCellEditorClassForNodeCell(
                name, childName, editorClass);
            CellEditClassManager.getInstance().addCellValidatorForNodeCell(
                name, childName, editorValidator);

            SettingNodeCell settingCell = new SettingNodeCell(childID,
                childName, childClassName, Categories.SETTING);

            if (child.getAttribute(TAG_DEFAULT_VALUE) != null) {
              // устанавливаем значение по умолчанию
              Object defaultValue = child
                  .createExecutableExtension(TAG_DEFAULT_VALUE);
              if (defaultValue instanceof IDefaultValue) {
                IDefaultValue dv = (IDefaultValue) defaultValue;
                ITemplateValueType value = dv.getDefaultValue();
                settingCell.setValue(value.get());
              }
            }

            nodeFactory.addNodeCell(settingCell);
          }

          NodeFactoryManager.getInstance().addNodeFactory(nodeFactory);

          ISimpleFuncFactory simpleFuncFactory = new SimpleFuncFactory(
              (ISimpleFunc) o);
          SimpleFuncFactoryManager.getInstance().addSimpleFuncFactory(
              simpleFuncFactory);
        }// if (o instanceof ISimpleFunc)
      } catch (InvalidRegistryObjectException e) {
        e.printStackTrace();
      } catch (CoreException e) {
        e.printStackTrace();
      }
    }// for(IConfigurationElement conf...
  }

  /**
   * Получить комментарии к функции из точки расширения
   *
   * @param conf
   *          - описание функции
   * @param commentAttribute
   *          - атрибут comment
   * @return - реальная строка с комментариями
   */
  private static String readCommentText(IConfigurationElement conf,
      String commentAttribute) {
    String resultCommentText = null;

    String bundleName = conf.getDeclaringExtension().getContributor().getName();
    Bundle bundle = Platform.getBundle(bundleName);
    if (bundle == null)
      return resultCommentText;

    String path = "";

    if ((commentAttribute == null) || ("".equals(commentAttribute))) {
      // если не указано обратное - берем коммент по умолчанию
      path = "comments/" + conf.getAttribute(TAG_CLASS);
    } else {
      path = commentAttribute;
    }

    URL commentURL = bundle.getEntry(path);
    if (commentURL == null)
      return resultCommentText;

    try {
      BundleURLConnection urlConnection = (BundleURLConnection) commentURL
          .openConnection();
      File cFile = new File(urlConnection.getFileURL().getFile());
      BufferedReader in = new BufferedReader(new FileReader(cFile));
      String str;

      StringBuilder sb = new StringBuilder("");
      while ((str = in.readLine()) != null) {
        sb.append(str);
        sb.append("\n");
      }
      String readedText = sb.toString();
      if (resultCommentText==null)
        resultCommentText = readedText;
      else if (!readedText.equals(""))
        resultCommentText += readedText;

      in.close();
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
    }

    // а вдруг это не просто текст, а properties-XML - тогда попытаемся распарсить
    resultCommentText = tryXMLDecode(resultCommentText);
    return resultCommentText;
  }

// Пример XML-описания Комментария к функции

//<?xml version="1.0" encoding="UTF-8" standalone="no"?>
//<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
//<properties>
//<comment>generated by DocGen</comment>
//<entry key="text">Вычитание двух скаляров</entry>
//</properties>
  private static String tryXMLDecode(String inputString) {
    String result = null;
    // попробуем распарсить строчку как properties-XML
    Properties prop = new Properties();
    try {
      InputStream is = new ByteArrayInputStream(inputString.getBytes());
      prop.loadFromXML(is);
      is.close();
      result = prop.getProperty(TAG_COMMENT_TEXT);
    } catch (IOException e) {
    }
    if (result == null)
      result = inputString;

    return result;
  }

  /**
   * Заполняет менеджер совместимых типов, т.е. тех, которые могут быть
   * преобразованы в другие типы данных
   *
   */

  @SuppressWarnings( { "unchecked", "static-access" })
  private void buildConverterExtension() {
    IExtensionRegistry registry = Platform.getExtensionRegistry();

    // Экземпляры типов данных, которые мы создадим, необходимо сохранить для
    // дальнейшего построения таблицы приводимости типов
    Set<ITemplateValueType> valueObjectsMap = new HashSet<ITemplateValueType>();

    // Ищем только НАШИ ('ru.spb.nicetu.altx.extpoint.point.typeConverter') расширения
    for (IConfigurationElement conf : registry
        .getConfigurationElementsFor(EXTPOINT_ID_typeConverter)) {
      try {
        Object o;
        try {
          o = conf.createExecutableExtension(TAG_CLASS);
        } catch (CoreException e) {
          e.printStackTrace();
          continue;
        }

        // Учитываем только те расширения, класс которых нас удовлетворяет
        if (!(o instanceof IConverter)) {
          continue;
        }

        IConverter converterInstance = (IConverter) o;

        // String className = classClass.toString();
        String converterClassName = conf.getAttribute(TAG_CLASS);
        System.out.println("Конвертер: " + converterClassName);

        String fromClassName = conf.getAttribute(TAG_FROM_CLASS);
        String toClassName = conf.getAttribute(TAG_TO_CLASS);

        Class<?> fromClass = null;
        Class<?> toClass = null;
        ClassLoader loader = converterInstance.getClass().getClassLoader();
        if (fromClassName != null) {
          try {
            fromClass = o.getClass().forName(fromClassName, true, loader);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
        }
        if (toClassName != null) {
          try {
            toClass = o.getClass().forName(toClassName, true, loader);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
        }
        if (fromClass != null && toClass != null) {
          ValueTypeConverterManager.getInstance().addType2TypeConverter(fromClass,
              toClass, converterInstance);
        }

      } catch (InvalidRegistryObjectException e) {
        e.printStackTrace();
      }
    }// for(IConfigurationElement conf : ...

  }
}
